package cn.com.kh;

import cn.com.elite.EliteBean;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @projectName: ngsapitask-hst
 * @package: cn.com.kh
 * @className: LogAnalyticJSSTrail
 * @author: LiJT
 * @description: TODO
 * @date: 2023-07-17 18:05
 * @version: 1.0
 */
public class LogAnalyticJSSTrail {
    private static final Logger log = LoggerFactory.getLogger(LogAnalyticJSSTrail.class);
    private static final String regInfo_LogName = "jss_logback"; // 日志文件名前缀

    private static final String regInfo_NoLine = "^\\d{4}-\\d{1,2}-\\d{1,2}"; // 非日志输出行

    private static final String regInfo_Start = "^2023-12.{3,}Dyn2CahceDo2.{1,}In Para:"; // 初始请求
    private static final String regInfo_SQL_N = "^2023-12.{3,}elite\\.core\\.dataBase\\.DBMethod.{3,}Dyn2CahceDo2.{1,}Begin Select Sql:"; // 标准无条件SQL
    private static final String regInfo_SQL_B = "^2023-12.{3,}elite\\.bussiness\\.dyn2\\.client\\.doLib\\.sub\\.Dyn2OneSql.{1,}LastBoundSql:"; // 有条件SQL
    private static final String regInfo_Rows = "^2023-12.{3,}elite\\.bussiness\\.dyn2\\.client\\.doLib\\.Dyn2CacheDo2Lib.{1,}End SelectSql  Rows:"; // 有条件SQL
    private static final String regInfo_UseTime = "^2023-12.{3,}elite\\.bussiness\\.dyn2\\.client\\.Dyn2CahceDo2.{1,}Use Time:"; // SQL耗时

    private static final String regInfo_DYN = "^2023-12.{3,}elite\\.bussiness\\.dyn2\\.root\\.Dyn2Root.{1,}初始化Dyn2 SQL结构 成功"; // 页面结构SQL

    private static final String[] skipKeys = "tab_dyn;update;insert;delete".toLowerCase().split(";");

    private static JdbcTemplate jt = TxConfig.getJdbcTemplate();

    public static void main(String[] args) throws Exception {

        jt = TxConfig.getJdbcTemplate();

        // 日志文件路径
        // String logDir = "D:\\归档日志信息\\应用服务日志\\50";
        String logDir = "D:\\归档日志信息\\应用服务日志\\50";
        log.debug("♻日志文件路径:" + logDir);

        List<String> fileList = ReadFile(logDir);
        log.debug("✅日志路径文件:" + fileList.toString());

        // 创建一个线程池，最多同时执行3个线程
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 30, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

        for (String logPath : fileList) {

            executor.execute(() -> {
                System.out.println("☢执行" + Thread.currentThread().getName() + "线程： " + doMainRun(logPath));
            });
        }

        // 关闭线程池
        executor.shutdown();

    }


    public static void doMain(EliteBean eliteBean, String logDir) throws Exception {

        jt = new JdbcTemplate(eliteBean.getDataSource());

        // 日志文件路径
        log.debug("♻日志文件路径:" + logDir);

        List<String> fileList = ReadFile(logDir);
        log.debug("✅日志路径文件:" + fileList.toString());

        // 创建一个线程池，最多同时执行3个线程
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 30, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

        for (String logPath : fileList) {

            executor.execute(() -> {
                log.debug("☢执行" + Thread.currentThread().getName() + "线程： " + doMainRun(logPath));
            });
        }

        // 关闭线程池
        executor.shutdown();

    }

    // 线程入口
    private static boolean doMainRun(String logPath) {
        boolean runRes = true;
        try {

            // 清空现有数据
            int num1 = jt.update("delete from mas_jss_token_request_trails where log_file_path = ?", logPath);
            int num2 = jt.update("delete from mas_jss_token_request_sqls_trails where log_file_path = ?", logPath);
            log.debug("❌清空现有数据:" + num1 + "/" + num2);

            loadFileDeal(logPath);

            log.debug("❌已处理完成删除日志文件:" + deleteFiles(logPath));
        } catch (Exception e) {
            runRes = false;
        }

        return runRes;
    }

    // 获取路径下所有的日志文件
    private static List<String> ReadFile(String fileDir) {
        List<String> fileList = new ArrayList<String>();
        File file = new File(fileDir);
        File[] files = file.listFiles();// 获取目录下的所有文件或文件夹
        if (files == null) {// 如果目录为空，直接退出
            return fileList;
        }
        // 遍历，目录下的所有文件
        for (File f : files) {
            if (f.isFile()) {
                if (f.getPath().toString().contains(regInfo_LogName)) {
                    fileList.add(f.getPath().toString());
                }
            } else if (f.isDirectory()) {
                System.out.println(f.getAbsolutePath());
                ReadFile(f.getAbsolutePath());
            }
        }
        return fileList;
    }

    // 加载并读取文件-核心
    public static void loadFileDeal(String logDir) {

        // 读取日志 - 调用时间 方法 结束时间 耗时
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(logDir))) {
            String line;
            Pattern p_NoLine = Pattern.compile(regInfo_NoLine);
            Pattern p_Start = Pattern.compile(regInfo_Start);
            Pattern p_SQL_N = Pattern.compile(regInfo_SQL_N);
            Pattern p_SQL_B = Pattern.compile(regInfo_SQL_B);
            Pattern p_Rows = Pattern.compile(regInfo_Rows);
            Pattern p_UseTime = Pattern.compile(regInfo_UseTime);
            Pattern p_DYN = Pattern.compile(regInfo_DYN);

            String para_type = "";
            String request_time = "";
            String request_thread_id = "";
            String request_thread_seq = "";
            StringBuffer para_sqls = new StringBuffer();

            long log_file_line = 0;

            while ((line = bufferedReader.readLine()) != null) {
                // 处理每一行数据 .....
                Matcher m_NoLine = p_NoLine.matcher(line);
                Matcher m_Start = p_Start.matcher(line);
                Matcher m_SQL_N = p_SQL_N.matcher(line);
                Matcher m_SQL_B = p_SQL_B.matcher(line);
                Matcher m_Rows = p_Rows.matcher(line);
                Matcher m_UseTime = p_UseTime.matcher(line);
                Matcher m_DYN = p_DYN.matcher(line);

                boolean b_NoLine = m_NoLine.find();
                boolean b_Start = m_Start.find();
                boolean b_SQL_N = m_SQL_N.find();
                boolean b_SQL_B = m_SQL_B.find();
                boolean b_Rows = m_Rows.find();
                boolean b_UseTime = m_UseTime.find();
                boolean b_DYN = m_DYN.find();

                if (!b_NoLine && !KHUtil.isNull(para_type)) {
                    para_sqls.append(System.lineSeparator());
                    para_sqls.append(line);

                } else {

                    // 如果已经是正常日志输出,提交保存
                    if (!KHUtil.isNull(para_sqls.toString())) {

                        // 剔除SQL含有系统表SQL
                        if (skipSQL(para_sqls.toString())) {
                            log.debug("⚠️请求时间:" + request_time + " 请求ID:" + request_thread_seq + " 线程号:" + request_thread_id + " para_sqls:" + para_sqls);
                            para_sqls.setLength(0);
                            para_type = "";

                        } else {
                            if (para_type.equals("b_SQL_N")) {

                                log.debug("✅请求时间:" + request_time + " 请求ID:" + request_thread_seq + " 线程号:" + request_thread_id + " para_sqls:" + para_sqls);

                                jt.update("insert into mas_jss_token_request_sqls_trails (request_time, request_thread_id, request_thread_seq, para_sqls, para_sqls_rows, log_file_path,log_file_line) values (?, ?, ?, ?, ?, ?, ?)", request_time, request_thread_id, request_thread_seq, para_sqls, "", logDir, log_file_line);
                                para_sqls.setLength(0);
                                para_type = "";

                            } else if (para_type.equals("b_SQL_B")) {

                                log.debug("☑请求时间:" + request_time + " 请求ID:" + request_thread_seq + " 线程号:" + request_thread_id + " para_sqls:" + para_sqls);

                                jt.update("insert into mas_jss_token_request_sqls_trails (request_time, request_thread_id, request_thread_seq, para_sqls, para_sqls_rows, log_file_path, log_file_line) values (?, ?, ?, ?, ?, ?, ?)", request_time, request_thread_id, request_thread_seq, para_sqls, "", logDir, log_file_line);
                                para_sqls.setLength(0);
                                para_type = "";

                            }
                        }
                    }

                    if (b_Start) {
                        // 开始行
                        request_time = line.substring(0, line.indexOf("["));
                        request_thread_id = line.substring(line.indexOf("[") + 1, line.indexOf("]"));
                        request_thread_seq = line.substring(line.indexOf("Dyn2CahceDo2:") + 13, line.indexOf("Dyn2CahceDo2:") + 26);
                        String request_db_method = "Dyn2CahceDo2";
                        String request_para = line.substring(line.indexOf("In Para:") + 8);

                        JSONObject ParaJson = JSONObject.fromObject(request_para);
                        String para_token = ParaJson.getString("token");
                        String para_dynId = ParaJson.getString("dynId");

                        log.debug("⭕请求Token:" + ParaJson.getString("token") + " DynID:" + ParaJson.getString("dynId") + " 请求时间:" + request_time + " 请求ID:" + request_thread_seq + " 线程号:" + request_thread_id + " InPara:" + request_para);

                        jt.update("replace into mas_jss_token_request_trails (request_time, request_thread_id, request_thread_seq, request_db_method, request_para, para_token, para_dynId, request_use_time, log_file_path, log_file_line) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", request_time, request_thread_id, request_thread_seq, request_db_method, request_para, para_token, para_dynId, "", logDir, log_file_line);

                    } else if (b_SQL_N) {
                        // 获取执行标准SQL
                        request_time = line.substring(0, line.indexOf("["));
                        request_thread_id = line.substring(line.indexOf("[") + 1, line.indexOf("]"));
                        request_thread_seq = line.substring(line.indexOf("Dyn2CahceDo2:") + 13, line.indexOf("Dyn2CahceDo2:") + 26);
                        para_sqls.setLength(0);
                        para_sqls.append(line.substring(line.indexOf("Begin Select Sql:") + 17));

                        para_type = "b_SQL_N";

                    } else if (b_SQL_B) {
                        // 获取绑定SQL
                        request_time = line.substring(0, line.indexOf("["));
                        request_thread_id = line.substring(line.indexOf("[") + 1, line.indexOf("]"));
                        request_thread_seq = line.substring(line.indexOf("Dyn2CahceDo2:") + 13, line.indexOf("Dyn2CahceDo2:") + 26);
                        para_sqls.setLength(0);
                        para_sqls.append(line.substring(line.indexOf("LastBoundSql:") + 13));

                        para_type = "b_SQL_B";

                    } else if (b_DYN) {
                        // 如果是动态页面SQL
                        request_time = line.substring(0, line.indexOf("["));
                        request_thread_id = line.substring(line.indexOf("[") + 1, line.indexOf("]"));
                        request_thread_seq = line.substring(line.indexOf("Dyn2CahceDo2:") + 13, line.indexOf("Dyn2CahceDo2:") + 26);
                        String para_sqls_rows = line.substring(line.indexOf("Rows:") + 5);

                        log.debug("⛔请求时间:" + request_time + " 请求ID:" + request_thread_seq + " 线程号:" + request_thread_id + " para_sqls_rows:" + para_sqls_rows);

                        jt.update("Update mas_Jss_Token_Request_Sqls_Trails Set status = -1 Where Request_Thread_Id = ? And Request_Thread_Seq = ? And Log_File_Path = ? And (Para_Sqls_Rows Is Null Or Para_Sqls_Rows = '') and status = 0 Limit 1", request_thread_id, request_thread_seq, logDir);

                    } else if (b_Rows) {
                        // 获取绑定SQL
                        request_time = line.substring(0, line.indexOf("["));
                        request_thread_id = line.substring(line.indexOf("[") + 1, line.indexOf("]"));
                        request_thread_seq = line.substring(line.indexOf("Dyn2CahceDo2:") + 13, line.indexOf("Dyn2CahceDo2:") + 26);
                        String para_sqls_rows = line.substring(line.indexOf("Rows:") + 5);

                        log.debug("⌛请求时间:" + request_time + " 请求ID:" + request_thread_seq + " 线程号:" + request_thread_id + " para_sqls_rows:" + para_sqls_rows);

                        jt.update("Update mas_Jss_Token_Request_Sqls_Trails Set Para_Sqls_Rows = ? Where Request_Thread_Id = ? And Request_Thread_Seq = ? And Log_File_Path = ? And (Para_Sqls_Rows Is Null Or Para_Sqls_Rows = '') and status = 0 Limit 1", para_sqls_rows, request_thread_id, request_thread_seq, logDir);

                    } else if (b_UseTime) {
                        // 获取绑定SQL
                        request_time = line.substring(0, line.indexOf("["));
                        request_thread_id = line.substring(line.indexOf("[") + 1, line.indexOf("]"));
                        request_thread_seq = line.substring(line.indexOf("Dyn2CahceDo2:") + 13, line.indexOf("Dyn2CahceDo2:") + 26);
                        String request_use_time = line.substring(line.indexOf("Use Time:") + 9, line.indexOf(" Out"));

                        log.debug("⏱请求时间:" + request_time + " 请求ID:" + request_thread_seq + " 线程号:" + request_thread_id + " request_use_time:" + request_use_time);

                        jt.update("Update mas_jss_token_request_trails Set Request_Use_Time = ? Where Request_Thread_Id = ? And Request_Thread_Seq = ? And Log_File_Path = ? And (Request_Use_Time Is Null Or Request_Use_Time = '') Limit 1", request_use_time, request_thread_id, request_thread_seq, logDir);

                    }
                }
                log_file_line++;
            }

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    // 获取路径下所有的日志文件
    private static boolean skipSQL(String SQL) {

        boolean skipFlag = false;
        // 遍历，目录下的所有文件
        for (String skipKey : skipKeys) {
            if (SQL.toLowerCase().contains(skipKey)) {
                skipFlag = true;
                break;
            }
        }
        return skipFlag;
    }

    // 完成后删除文件
    public static boolean deleteFiles(String pathName) {
        boolean flag = false;
        //根据路径创建文件对象
        File file = new File(pathName);
        //路径是个文件且不为空时删除文件
        if (file.isFile() && file.exists()) {
            flag = file.delete();
        }
        //删除失败时，返回false
        return flag;
    }

}
